package com.arpa.oms.service.impl;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.oms.domain.dto.OmsPayableDTO;
import com.arpa.oms.domain.dto.OmsSettlementEditDTO;
import com.arpa.oms.domain.dto.OmsSettlementItemEditDTO;
import com.arpa.oms.domain.entity.OmsPayable;
import com.arpa.oms.domain.entity.OmsPayableDetail;
import com.arpa.oms.domain.entity.OmsPayableItem;
import com.arpa.oms.domain.enums.PayableAndReceivableStatusEnum;
import com.arpa.oms.domain.vo.OmsPayableVO;
import com.arpa.oms.mapper.OmsPayableMapper;
import com.arpa.oms.service.IOmsPayableDetailService;
import com.arpa.oms.service.IOmsPayableItemService;
import com.arpa.oms.service.IOmsPayableService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

import static com.arpa.oms.domain.enums.PayableAndReceivableStatusEnum.NO_PASS;
import static com.arpa.oms.domain.enums.PayableAndReceivableStatusEnum.PASS;

/**
 * <p>
 * OMS付款单 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-12-04
 */
@Service
@Log4j2(topic = "business")
public class OmsPayableServiceImpl extends ServiceImpl<OmsPayableMapper, OmsPayable> implements IOmsPayableService {


    @Resource
    private PartyCache partyCache;
    private final IOmsPayableItemService omsPayableItemService;
    private final IOmsPayableDetailService omsPayableDetailService;

    public OmsPayableServiceImpl(IOmsPayableItemService omsPayableItemService,
                                 IOmsPayableDetailService omsPayableDetailService) {
        this.omsPayableItemService = omsPayableItemService;
        this.omsPayableDetailService = omsPayableDetailService;
    }


    /**
     * 保存
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean save(OmsPayable entity) {
        if (StringUtils.isBlank(entity.getCode())) {
            entity.setCode(IdUtil.simpleUUID());
        }
        if (StringUtils.isBlank(entity.getCreatedBy())) {
            entity.setCreatedBy(UserUtil.getCode());
        }
        return super.save(entity);
    }

    /**
     * 根据code更新实体
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(OmsPayable entity) {
        if (StringUtils.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
        }
        return baseMapper.update(entity, new QueryWrapper<OmsPayable>().lambda().eq(OmsPayable::getCode, entity.getCode()));
    }

    /**
     * 查询列表
     *
     * @param omsPayableDTO
     * @return
     */
    @Override
    public List<OmsPayableVO> queryList(OmsPayableDTO omsPayableDTO) {
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(omsPayableDTO.getSortField())) {
            omsPayableDTO.setSortField(CommonUtil.camel2Underline(omsPayableDTO.getSortField()));
        }
        List<OmsPayableVO> omsPayableVOList = baseMapper.queryList(omsPayableDTO);
        return omsPayableVOList;
    }

    /**
     * 查询合计,包含总数
     *
     * @param omsPayableDTO
     * @return
     */
    @Override
    public OmsPayableVO queryListSum(OmsPayableDTO omsPayableDTO) {
        OmsPayableVO omsPayableVO = baseMapper.queryListSum(omsPayableDTO);
        return omsPayableVO;
    }

    /**
     * 通过付款单号获取付款单信息，包含付款明细
     *
     * @param code
     * @return
     */
    @Override
    public OmsPayable getOmsPayable(String code) {
        OmsPayable entity = super.getOne(new QueryWrapper<OmsPayable>().lambda().eq(OmsPayable::getCode, code)
                .eq(OmsPayable::getShipmentCode, UserUtil.getShipmentCompanyCode()));
        if (null == entity) {
            log.error("查询付款单【{}】详情失败，找不到付款单信息", code);
            throw new ServiceException("查询付款单【" + code + "】详情失败，找不到付款单信息");
        }


        List<OmsPayableItem> omsPayableItems = omsPayableItemService.listByPayableCode(code);
        entity.setOmsPayableItems(omsPayableItems);
        return entity;
    }


    /**
     * 更新付款单
     * 关联更改费用明细
     *
     * @param settlementEditDTO
     * @return
     */
    @Override
    public void update(final OmsSettlementEditDTO settlementEditDTO) {
// TODO: 2021/7/26 暂无 减免金额 字段

        //更新收款单明细
        List<OmsSettlementItemEditDTO> settlementItems = settlementEditDTO.getSettlementItems();
        // 主单减免金额
        final BigDecimal[] amountReduce = {BigDecimal.ZERO};
        // 主单结算金额
        final BigDecimal[] amountThisSettled = {BigDecimal.ZERO};
        if (IterUtil.isNotEmpty(settlementItems)) {
            settlementItems.forEach(r -> {

                // 计算减免总金额
                amountReduce[0] = NumberUtil.add(amountReduce[0], r.getReduceMoney());
                // 计算结算总金额
                amountThisSettled[0] = NumberUtil.add(amountThisSettled[0],r.getNewThisSettledMoney());

//                // 校验减免金额与结算金额大小
                if (!NumberUtil.isLessOrEqual(r.getReduceMoney(),r.getNewThisSettledMoney())&& r.getReduceMoney().compareTo(BigDecimal.ZERO)>0){
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】减免金额需小于等于结算金额", r.getSettlementCode(), r.getCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】减免金额需小于等于结算金额");
                }

                //结算单明细
                final OmsPayableItem omsPayableItem = omsPayableItemService.getOne(new QueryWrapper<OmsPayableItem>().lambda().eq(OmsPayableItem::getCode, r.getCode()));
                if (null == omsPayableItem) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】找不到", r.getSettlementCode(), r.getCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】找不到");
                }

                //费用信息
                OmsPayableDetail expenseDetail = omsPayableDetailService.getOne(new QueryWrapper<OmsPayableDetail>().lambda()
                        .eq(OmsPayableDetail::getCode, r.getExpenseDetailCode()).eq(OmsPayableDetail::getShipmentCode, UserUtil.getShipmentCompanyCode()));
                if (expenseDetail == null) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】对应的费用信息【{}】找不到", r.getSettlementCode(), r.getCode(), r.getExpenseDetailCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】对应的费用信息【" + r.getExpenseDetailCode() + "】找不到");
                }


                //判断修改后的计算金额是否小于0
                if (r.getNewThisSettledMoney().compareTo(BigDecimal.ZERO) == 0) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额不能等于0", r.getSettlementCode(), r.getCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】结算金额不能等于0");
                }
                //判断修改后的计算金额是否小于0
                //计算付款明细未结算金额
                BigDecimal unsettledMoney =expenseDetail.getAmountMoney().subtract(expenseDetail.getSettledMoney()).subtract(expenseDetail.getWaitingSettledMoney()).add(omsPayableItem.getThisSettledMoney());
                //未结算金额为负值，则本次结算金额必须小于0
                if(unsettledMoney.compareTo(BigDecimal.ZERO) < 0){
                    if (r.getNewThisSettledMoney().compareTo(BigDecimal.ZERO) > 0) {
                        log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额必须小于0", r.getSettlementCode(), r.getCode());
                        throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】结算金额必须小于0");
                    }
                }   //未结算金额为正值，则本次结算金额必须大于0
                if(unsettledMoney.compareTo(BigDecimal.ZERO) > 0){
                    if (r.getNewThisSettledMoney().compareTo(BigDecimal.ZERO) < 0) {
                        log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额必须大于0", r.getSettlementCode(), r.getCode());
                        throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】结算金额必须大于0");
                    }
                }

                if (r.getNewThisSettledMoney().abs().compareTo(unsettledMoney.abs()) > 0) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额不能大于付款明细未结算金额【{}】", r.getSettlementCode(), r.getCode(),unsettledMoney);
                    throw new ServiceException("结算单修改失败：结算单【" + r.getSettlementCode() + "】结算明细【" + r.getCode() + "】结算金额不能大于付款明细未结算金额【"+unsettledMoney+"】");
                }




                //还原更新费用信息
                BigDecimal restoreSettledMoney = omsPayableItem.getThisSettledMoney().subtract(r.getNewThisSettledMoney());
                // 还原减免费用
                BigDecimal restoreReduceMoney = omsPayableItem.getReduceMoney().subtract(r.getReduceMoney());
                omsPayableDetailService.restoreSettlementStatus(expenseDetail.getCode(), restoreSettledMoney,BigDecimal.ZERO);

                //更新费用明细
                omsPayableItemService.update(new UpdateWrapper<OmsPayableItem>().lambda()
                        .eq(OmsPayableItem::getCode, r.getCode())
                        .set(OmsPayableItem::getThisSettledMoney, r.getNewThisSettledMoney())
                        .set(OmsPayableItem::getReduceMoney, r.getReduceMoney())
                );


            });
        }


        //要删除的收款单明细
        if (IterUtil.isNotEmpty(settlementEditDTO.getRemoveItemCodes())) {
            List<OmsPayableItem> omsPayableItems = omsPayableItemService.list(new QueryWrapper<OmsPayableItem>().lambda().in(OmsPayableItem::getCode, settlementEditDTO.getRemoveItemCodes()));
            if (IterUtil.isNotEmpty(omsPayableItems)) {
                //获取费用明细
                omsPayableItems.forEach(r -> {
                    //费用信息
                    OmsPayableDetail expenseDetail = omsPayableDetailService.getOne(new QueryWrapper<OmsPayableDetail>().lambda().eq(OmsPayableDetail::getCode, r.getPayableDetailCode()));
                    if (expenseDetail == null) {
                        log.error("结算单修改失败：费用信息【{}】找不到", r.getPayableDetailCode());
                        throw new ServiceException("结算单删除失败：费用详情【" + r.getPayableDetailCode() + "】找不到");
                    }
                    //还原更新费用信息
                    omsPayableDetailService.restoreSettlementStatus(r.getPayableDetailCode(), r.getThisSettledMoney(), BigDecimal.ZERO);
                });
                //删除收款单明细
                omsPayableItemService.remove(new QueryWrapper<OmsPayableItem>().lambda().in(OmsPayableItem::getCode, settlementEditDTO.getRemoveItemCodes()));
            }
        }

        // 主单付款金额
        BigDecimal amountCollected = NumberUtil.sub(amountThisSettled[0],amountReduce[0]);
//        BigDecimal amountCollected = NumberUtil.sub(amountThisSettled[0]);

        //更新结算单主表
        update(new UpdateWrapper<OmsPayable>().lambda()
                .eq(OmsPayable::getCode, settlementEditDTO.getCode())
                .eq(OmsPayable::getShipmentCode, UserUtil.getShipmentCompanyCode())
                .set(OmsPayable::getRemarks, settlementEditDTO.getRemarks())
                .set(OmsPayable::getModifiedBy, UserUtil.getCode())
                .set(OmsPayable::getStatus, settlementEditDTO.getStatus())
                .set(OmsPayable::getAmountMoney, amountThisSettled[0]) // 结算金额
                .set(OmsPayable::getAmountReduce, amountReduce[0]) // 减免金额
                .set(OmsPayable::getAmountCollected, amountCollected) // 收款金额
                .set(OmsPayable::getModifiedName, partyCache.translate(UserUtil.getCode()))
        );
    }


    /**
     * 删除付款单
     * 删除付款单的同时，更新费明细状态
     *
     * @param code
     */
    @Override
    public void removeByCode(String code) {
        OmsPayable omsPayable = super.getOne(new QueryWrapper<OmsPayable>().lambda().eq(OmsPayable::getCode, code).eq(OmsPayable::getShipmentCode,UserUtil.getShipmentCompanyCode()));

        if (null == omsPayable) {
            log.error("付款单删除失败：付款单【{}】找不到", code);
            throw new ServiceException("付款单【"+code+"】找不到");
        }
        if (!PayableAndReceivableStatusEnum.NEWS.equals(omsPayable.getStatus())) {
            log.error("付款单删除失败：付款单【{}】已提交不能删除", code);
            throw new ServiceException("付款单【"+code+"】已提交不能删除");
        }
        //TODO 增加状态判断
        //获取收款单明细
        List<OmsPayableItem> omsPayableItems = omsPayableItemService.list(new QueryWrapper<OmsPayableItem>().lambda().eq(OmsPayableItem::getPayableCode, code));
        //获取费用明细
        omsPayableItems.forEach(r -> {
            //费用信息
            OmsPayableDetail expenseDetail = omsPayableDetailService.getOne(new QueryWrapper<OmsPayableDetail>().lambda().eq(OmsPayableDetail::getCode, r.getPayableDetailCode()));
            if (expenseDetail == null) {
                log.error("结算单修改失败：费用信息【{}】找不到", r.getPayableDetailCode());
                throw new ServiceException("结算单删除失败：费用详情【" + r.getPayableDetailCode() + "】找不到");
            }
            //还原更新费用信息
//            omsPayableDetailService.restoreSettlementStatus(r.getPayableDetailCode(), r.getThisSettledMoney(), r.getReduceMoney());
            omsPayableDetailService.restoreSettlementStatus(r.getPayableDetailCode(), r.getThisSettledMoney(), BigDecimal.ZERO);

        });
        //删除收款单
        super.remove(new QueryWrapper<OmsPayable>().lambda().eq(OmsPayable::getCode, code));
        //删除收款单明细
        omsPayableItemService.remove(new QueryWrapper<OmsPayableItem>().lambda().eq(OmsPayableItem::getPayableCode, code));

    }

    /**
     * 收款单审核
     *
     * @param code   收款单code
     * @param status 审核状态 1：通过。2不通过
     * @param verifyOpinion 审核意见
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean approve(String code, PayableAndReceivableStatusEnum status,String verifyOpinion) {
        update(new UpdateWrapper<OmsPayable>().lambda().eq(OmsPayable::getCode, code)
                .set(OmsPayable::getModifiedBy, UserUtil.getCode())
                .set(OmsPayable::getStatus, status)
                .set(OmsPayable::getVerifyOpinion, verifyOpinion)
                .set(OmsPayable::getVerifyBy, UserUtil.getCode())
                .set(OmsPayable::getVerifyName, partyCache.translate(UserUtil.getCode()))
                .set(OmsPayable::getGmtVerify, LocalDateTime.now())
                .set(OmsPayable::getModifiedName, partyCache.translate(UserUtil.getCode()))
        );

        //获取收款单明细
        List<OmsPayableItem> omsPayableItems = omsPayableItemService.list(new QueryWrapper<OmsPayableItem>().lambda().eq(OmsPayableItem::getPayableCode, code));
        if (PASS.equals(status)) {
            omsPayableItems.forEach(r -> {
                //确认费用结算信息
                omsPayableDetailService.confirmSettlementStatus(r.getPayableDetailCode(), r.getThisSettledMoney());
                //审核通过更新 未结算金额=总金额-已结算金额-本次结算金额
                omsPayableItemService.update(new UpdateWrapper<OmsPayableItem>().lambda()
                .eq(OmsPayableItem::getPayableCode,r.getPayableCode())
                .setSql("unsettled_money=amount_money-settled_money-this_settled_money"));
            });
        }
        if (NO_PASS.equals(status)) {
            omsPayableItems.forEach(r -> {
                //还原更新费用信息
//                omsPayableDetailService.restoreSettlementStatus(r.getPayableDetailCode(), r.getThisSettledMoney(),r.getReduceMoney());
                omsPayableDetailService.restoreSettlementStatus(r.getPayableDetailCode(), r.getThisSettledMoney(),BigDecimal.ZERO);
            });
        }


        return true;
    }
}
